home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C ++ / Applications / FlyThrough 1.1.2 / src / Source / Grayscale Classes / CAGASlider.cp < prev    next >
Encoding:
Text File  |  1997-04-02  |  26.1 KB  |  932 lines  |  [TEXT/CWIE]

  1. //
  2. //    CAGASlider.cp
  3. //
  4. //    Apple Grayscale Appearance Slider class for PowerPlant
  5. //    Subclassed from LControl
  6. //
  7. //    version 1.4.4 - April 2, 1997
  8. //
  9. //    Copyright © 1996-1997 by James Jennings. All rights reserved.
  10. //
  11. /*
  12.     Optimization notes: I basically did three things (in slider v1.3.1)
  13.         1. Calculated the smallest rectangle to redraw while tracking the mouse.
  14.                 (stored in mChangedRect)
  15.         2. Don't draw tick marks while tracking the mouse.
  16.                 (This assumes the tick marks won't change while tracking.)
  17.         3. Precalculated the value returned by GetTrackRect() and cached it. 
  18.                 (stored in mTrackRect)
  19.     
  20.     The StOffscreenGWorld constructor calls EraseRect() unnecessarily 
  21.     since I later erase everything to a light gray.
  22.     I can't fix this without rewriting StOffscreenGWorld and have declined to 
  23.     do this at present. This could be done later if necessary.
  24. */
  25.  
  26. //    A thought: 
  27. // We can replace mIsTracking with IsTracking() { return GetValue() != mTrackingValue; }
  28.  
  29. // Uncomment the next line to turn on profiling for drawing and tracking.
  30. //#include <profiler.h>
  31.  
  32. #include "CAGASlider.h"
  33. #include <LStream.h>
  34. #include <UDrawingUtils.h>
  35. #include <UDrawingState.h>
  36. #include <PP_Messages.h>
  37. #include <UGWorld.h>
  38.  
  39. // Note that the flag AGA_VERSION won't change the indicator/thumb. 
  40. // You have to replace the PICT 1000 resource for that.
  41. //#define AGA_VERSION 1    // Apple's specs as of April 96
  42. #define AGA_VERSION 2    // Apple's specs as of Sept 96
  43.  
  44. // constants that control the geometry of the slider
  45. static const Int16 kEndMargin = 10;        // where the indicator stops.
  46. static const Int16 kFlatMargin = 4;        // margin on the flat side on an indicator
  47. static const Int16 kPointMargin = 6;    // margin on the pointy side--not including tickmarks
  48. static const Int16 kTrackWidth = 5;        // width of the indicator's track
  49. static const Int16 kOffsideCutoff = 24;    // abort by dragging this far from control
  50. static const Int16 kTrackToTick = 10;    // center of track to start of tick mark
  51. static const Int16 kTickLen = 6;        // length of tick mark
  52. static const Int16 kIndSize = 16;        // size of indicator image
  53. static const Int16 kDepthCutoff = 4;// 4 or 8. The screen depth to start grayscale drawing.
  54.  
  55. // If there are no tick marks, the slider is 16 pixels wide.
  56. // If there are tick marks, the slider is approximately...
  57. //const Int16 kSliderWidth = kFlatMargin+kTrackWidth+kTrackToTick+kTickLen; // = 25 pixels
  58.  
  59. // Class variables for holding the indicator image data.
  60. LGWorld    *    CAGASlider::sColorData = 0;
  61. LGWorld    *    CAGASlider::sBlackAndWhiteData = 0;
  62. LGWorld    *    CAGASlider::sMaskData = 0;
  63.  
  64. // Indicator image related constants.
  65. const Rect         kPICTDataSize = { 0, 0, 6*kIndSize, 4*kIndSize };
  66. const ResIDT    k8BitPICTid = 1000;
  67. const ResIDT    k1BitPICTid = 1001;
  68. const ResIDT    kMaskPICTid = 1002;
  69.  
  70. const RGBColor kDefaultBackground = {0xDDDD, 0xDDDD, 0xDDDD};
  71.  
  72. CAGASlider*    CAGASlider::CreateFromStream(LStream *inStream)
  73. {
  74.     return new CAGASlider( inStream );
  75. }
  76.  
  77. CAGASlider::CAGASlider(LStream *inStream)
  78.     : LControl( inStream ), mState(ind_Enabled), mContinuousBroadcast(true),
  79.       mIsTracking(false), mTrackingValue(0), mMinIsBottomOrRight(false),
  80.       mBackground( kDefaultBackground )
  81. {
  82.     GetColor( ga_Background, mBackground );
  83.     
  84.     Int32 directionCode;
  85.     inStream->ReadBlock( &directionCode, sizeof(directionCode) );
  86.     
  87.     // Convert the entered code to the enumerated type
  88.     switch ( directionCode ) {
  89.     case 'hori':    mDirection = ind_Horizontal;    break;
  90.     case 'vert':    mDirection = ind_Vertical;        break;
  91.     case 'nort':    mDirection = ind_North;            break;
  92.     case 'sout':    mDirection = ind_South;            break;
  93.     case 'east':    mDirection = ind_East;            break;
  94.     case 'west':    mDirection = ind_West;            break;
  95.     default:    // anything else (like 'prop'), base on the proportions of the pane
  96.         SDimension16 theSize;
  97.         GetFrameSize( theSize );
  98.         if ( theSize.width > theSize.height ) mDirection = ind_Horizontal;
  99.         else mDirection = ind_Vertical;
  100.         break;
  101.     }
  102.     
  103.     // If mNumberOfTicks = 0, don't display any tick marks
  104.     // If mNumberOfTicks = 1, display one tick mark per value
  105.     // If mNumberOfTicks > 1, then mNumberOfTicks is the number of tickmarks (intervals + 1)
  106.     inStream->ReadBlock( &mNumberOfTicks, sizeof(mNumberOfTicks) );
  107.     
  108.     // mContinuousBroadcast == true -> broadcast change everytime the indicator moves
  109.     // mContinuousBroadcast == false -> wait for mouseUp
  110.     inStream->ReadBlock( &mContinuousBroadcast, sizeof(mContinuousBroadcast) );
  111.     
  112.     // mMinIsBottomOrRight == false -> mValue increases as you slide down or right
  113.     //        Behaves like a standard scrollbar. 
  114.     // mMinIsBottomOrRight == true -> mValue increases as you slide up or left
  115.     //        Is more intuitive for vertical sliders.
  116.     inStream->ReadBlock( &mMinIsBottomOrRight, sizeof(mMinIsBottomOrRight) );
  117.     
  118.     // The background color.
  119.     // This is usually ga_2, but might be ga_1 in a Tabbed pane.
  120.     // Apple's specs don't actually specify a background color for the controls.
  121.     inStream->ReadBlock( &mBackground, sizeof(RGBColor) );
  122.     
  123.     // do we need this line?
  124. //    mState = ( (mEnabled != triState_Off) ? ind_Enabled : ind_Disabled);
  125.     
  126.     CalcTrackRect();
  127.     CalcLocalFrameRect(mChangedRect);    // reasonable default value
  128. }
  129.  
  130. CAGASlider::CAGASlider(const CAGASlider &inSlider)    // copy constructor
  131.     : LControl( inSlider ), mState( ind_Enabled ), mIsTracking( false ), mTrackingValue( 0 ), 
  132.       mDirection( inSlider.mDirection ), 
  133.       mNumberOfTicks( inSlider.mNumberOfTicks ),
  134.       mContinuousBroadcast( inSlider.mContinuousBroadcast ), 
  135.       mMinIsBottomOrRight( inSlider.mMinIsBottomOrRight ),
  136.       mBackground( inSlider.mBackground )
  137. {
  138.     CalcTrackRect();
  139.     CalcLocalFrameRect(mChangedRect);    // reasonable default value
  140. }
  141.  
  142. CAGASlider::~CAGASlider()
  143. {
  144. }
  145.  
  146. void CAGASlider::Purge()
  147. {    // Release the memory containing the indicator images. (Not directly used by the class.)
  148.     // Call it when memory is low, or when a window full of sliders is closed.
  149.     // It doesn't hurt to call this too often, since the sliders will reallocate 
  150.     // the images if necessary. 
  151.     // 
  152.     // If you don't want to be aggressive about reclaiming memory, 
  153.     // you don't have to call this at all. The images only take a few K.
  154.     
  155.     if ( sColorData != nil ) {
  156.         delete sColorData;
  157.         sColorData = nil;
  158.     }
  159.     
  160.     if ( sBlackAndWhiteData != nil ) {
  161.         delete sBlackAndWhiteData;
  162.         sBlackAndWhiteData = nil;
  163.     }
  164.     
  165.     if ( sMaskData != nil ) {
  166.         delete sMaskData;
  167.         sMaskData = nil;
  168.     }
  169. }
  170.  
  171. void CAGASlider::DrawSelf(void)
  172. {
  173. #ifdef __PROFILER__
  174.     Boolean profilerIsOn = ProfilerGetStatus();
  175.     ProfilerSetStatus(true);    // turn on the profiler
  176. #endif
  177.     Rect theFrame, r;
  178.     if (mIsTracking) {
  179.         // optimize by only drawing what changes (new in slider v 1.3.1
  180.         theFrame = mChangedRect;
  181.     } else {
  182.         CalcLocalFrameRect( theFrame );
  183.     }
  184.     
  185.     StDeviceLoop devices(theFrame);
  186.     Int16 depth;
  187.  
  188.     while (devices.NextDepth(depth)) {
  189.     
  190.         Boolean useGrays = (depth >= kDepthCutoff);
  191.         
  192.         // find the overlap between the frame and the current device
  193.         r = (*devices.GetCurrentDevice())->gdRect;
  194.         ::GlobalToLocal(&topLeft(r));
  195.         ::GlobalToLocal(&botRight(r));
  196.         if ( !::SectRect( &theFrame, &r, &r ) ) continue;
  197.         
  198.         // Detect the background color so we may fill the offscreen world correctly.
  199.         // (didn't work when window was partially covered)
  200.     //    if ( useGrays )
  201.     //        DetectBackgroundColor();
  202.             
  203.         // Use an offscreen world to avoid flickering
  204.         StOffscreenGWorld offscreen( r, depth );
  205.         
  206.         DrawBackground( r, useGrays );
  207.         
  208.         DrawSelfBasic(useGrays);
  209.             
  210.     }
  211. #ifdef __PROFILER__
  212.     ProfilerSetStatus(profilerIsOn);    // turn off the profiler
  213. #endif
  214. }
  215.  
  216. void CAGASlider::DrawSelfBasic( Boolean useGrays )
  217. {
  218.     StColorPenState::Normalize();
  219.     
  220.     EIndicatorState theState = ( IsEnabled() ? mState : ind_Disabled );
  221.     
  222.     DrawTrack( theState, useGrays );
  223.     
  224.     if ( !mIsTracking )    // don't need ticks while tracking indicator
  225.         DrawTickMarks( mNumberOfTicks, theState, useGrays );
  226.     
  227.     if ( mIsTracking )    // Draw ghost indicator
  228.         DrawIndicator( mTrackingValue, ind_Ghost, useGrays );
  229.     
  230.     // Draw indicator *over* the ghost if they overlap.
  231.     DrawIndicator( GetValue(), theState, useGrays );
  232. }
  233.  
  234. void CAGASlider::DrawTrack( EIndicatorState inState, Boolean inUseGrays )
  235. {
  236.     StColorPenState::Normalize();
  237.     
  238.     Rect r;
  239.     GetTrackRect( r );
  240.     
  241.     const Int16 kRoundness = 4;
  242.     
  243.     // Colors depend on inState
  244.     EGAColor black, gray;
  245.     if ( inState == ind_Disabled ) {
  246.         gray = ga_4;
  247.         black = ga_8;
  248.     } else {
  249.         gray = ga_5;
  250.         black = ga_Black;
  251.     }
  252.  
  253.     if ( inUseGrays ) {
  254. #if (AGA_VERSION==2)
  255.         if (inState != ind_Disabled) {
  256. #endif
  257.             // Draw white and gray hilites
  258.             r.bottom++;
  259.             r.right++;
  260.             SetForeColor( ga_White );
  261.             ::PaintRoundRect( &r, kRoundness, kRoundness );
  262.             
  263.             ::OffsetRect( &r, -1, -1 );
  264.             SetForeColor( gray );
  265.             ::PaintRoundRect( &r, kRoundness, kRoundness );
  266.             
  267.             r.top++;
  268.             r.left++;
  269. #if (AGA_VERSION==2)
  270.         } else {
  271.         
  272.             // A disabled track still needs a fill color
  273.             SetForeColor( gray );
  274.             ::PaintRoundRect( &r, kRoundness, kRoundness );
  275.             
  276.         }
  277. #endif
  278.         SetForeColor( black );
  279.     } else {
  280.         if (inState == ind_Disabled) 
  281.             ::PenPat(&UQDGlobals::GetQDGlobals()->gray);
  282.     }
  283.     ::FrameRoundRect( &r, kRoundness, kRoundness );
  284. }
  285.  
  286. void CAGASlider::DrawIndicator( Int32 inValue, EIndicatorState inState, Boolean inUseGrays )
  287. {
  288.     // where to draw it
  289.     Rect r;
  290.     ValueToIndicatorRect( inValue, r );
  291.     
  292.     //Commented out old version of the code: It didn't work in all cases.
  293.     //ResIDT    icon = ind_First_ics8_ID + inDirection * ind_StateOffset + inState;
  294.     //DrawIcon1( r, icon, inUseGrays ); break;
  295.     
  296.     DrawIcon( r, mDirection, inState, inUseGrays );
  297. }
  298.  
  299. /*
  300.     DrawIcon1() was my prefered method of drawing the indicator.
  301.     It did not work on all monitors on all machines.
  302.     It also failed under QuickDrawGX.
  303.     Is there a bug in the PPC version of the Toolbox code?
  304.     
  305. void    CAGASlider::DrawIcon1( Rect inWhere, ResIDT inWhich, Boolean inUseGrays )
  306. {
  307.     OSErr anErr;
  308.     // PlotIconID() needs a 16x16 rectangle.
  309.     inWhere.bottom = inWhere.top + kIndSize;
  310.     inWhere.right = inWhere.left + kIndSize;
  311.     
  312.     anErr = ::PlotIconID( &inWhere, atNone, ttNone, inWhich );
  313.     ThrowIfOSErr_( anErr );
  314. }
  315. */
  316.  
  317. void    CAGASlider::DrawIcon( Rect inWhere, EIndicatorDirection inDirection, 
  318.                             EIndicatorState inState, Boolean inUseGrays )
  319. {    // draw the indicator out of a PICT
  320.     
  321.     // PlotIconID() needs a 16x16 rectangle.
  322.     inWhere.bottom = inWhere.top + kIndSize;
  323.     inWhere.right = inWhere.left + kIndSize;
  324.  
  325.     // we initialize the data we need (only does something on the first call)
  326.     if ( inUseGrays ) InitColorData();
  327.     else InitBlackAndWhiteData();
  328.     
  329.     GWorldPtr imageWorld = (inUseGrays ? sColorData : sBlackAndWhiteData )->GetMacGWorld();
  330.     GWorldPtr maskWorld = sMaskData->GetMacGWorld();
  331.     Assert_( imageWorld != nil && maskWorld != nil );
  332.     
  333.     PixMapHandle image = ::GetGWorldPixMap( imageWorld );
  334.     PixMapHandle mask  = ::GetGWorldPixMap( maskWorld );
  335.     ThrowIfNil_( image );
  336.     ThrowIfNil_( mask );
  337.     
  338.     Boolean notPurged;
  339.     notPurged = ::LockPixels( image );
  340.     notPurged = ::LockPixels( mask );
  341.     
  342.     Rect r = {0,0,kIndSize,kIndSize};
  343.     Rect maskRect = r;
  344.     ::OffsetRect( &r, inState * kIndSize, inDirection * kIndSize );
  345.     ::OffsetRect( &maskRect, 0, inDirection * kIndSize );
  346.     
  347.     GrafPtr thePort;
  348.     ::GetPort( & thePort );
  349.     
  350.     ::ForeColor( blackColor );
  351.     ::BackColor( whiteColor );
  352.     //    ::CopyMask( srcBits, maskBits, dstBits, srcRect, maskRect, dstRect );
  353.     ::CopyMask( (BitMapPtr)*image, (BitMapPtr)*mask, &(thePort->portBits), 
  354.                     &r, &maskRect, &inWhere );
  355.     
  356.     ::UnlockPixels( image );
  357.     ::UnlockPixels( mask );
  358. }
  359.  
  360. void    CAGASlider::InitColorData()
  361. {
  362.     InitMaskData();
  363.     
  364.     if ( sColorData != nil ) return;
  365.     
  366.     sColorData = new LGWorld( kPICTDataSize, 8 );
  367.     ThrowIfNil_( sColorData );
  368.     
  369.     InitPICTDataHelper( sColorData, k8BitPICTid, kPICTDataSize );
  370. }
  371.  
  372. void    CAGASlider::InitBlackAndWhiteData()
  373. {
  374.     InitMaskData();
  375.     
  376.     if ( sBlackAndWhiteData != nil ) return;
  377.     
  378.     sBlackAndWhiteData = new LGWorld( kPICTDataSize, 1 );
  379.     ThrowIfNil_( sBlackAndWhiteData );
  380.     
  381.     InitPICTDataHelper( sBlackAndWhiteData, k1BitPICTid, kPICTDataSize );
  382. }
  383.  
  384. void    CAGASlider::InitMaskData()
  385. {
  386.     if ( sMaskData != nil ) return;
  387.     
  388.     Rect r = kPICTDataSize;
  389.     r.right = r.left + kIndSize;
  390.     sMaskData = new LGWorld( r, 1 );
  391.     ThrowIfNil_( sMaskData );
  392.     
  393.     InitPICTDataHelper( sMaskData, kMaskPICTid, r );
  394. }
  395.  
  396. void    CAGASlider::InitPICTDataHelper( LGWorld *inWorld, const ResIDT inPICT, const Rect inRect )
  397. {
  398.     PicHandle aPicH = ::GetPicture( inPICT );
  399.     ThrowIfNil_( aPicH );
  400.     
  401.     inWorld->BeginDrawing();
  402.     ::DrawPicture( aPicH, &inRect );
  403.     inWorld->EndDrawing();
  404.     
  405.     ::ReleaseResource( (Handle)aPicH );
  406. }
  407. /*
  408.     DetectBackgroundColor() removed 3/29/1997
  409.     It didn't work when the window was partially covered.
  410.     (e.g. by a floating window.)
  411. void    CAGASlider::DetectBackgroundColor()
  412. {
  413.     // The Problem:
  414.     // Alas, the slider will not always be drawn on a ga_2 background.
  415.     // The "Front Tab" pane has a ga_1 background.
  416.     // To solve this problem, I will attempt to detect the background color
  417.     // that is presumably drawn by the enclosing view.
  418.     
  419.     Rect trackRect;
  420.     GetTrackRect( trackRect );
  421.     
  422.     // There should be a background pixel just beyond the top left corner of the trackRect
  423.     // either when the slider is first drawn, or when tracking the mouse.
  424.     ::GetCPixel( trackRect.left - 1, trackRect.top - 1, &mBackground);
  425. }*/
  426.  
  427. void    CAGASlider::DrawBackground( const Rect &inRect, Boolean inUseGrays )
  428. {
  429.     // Fill in the offscreen world with the background color.
  430.     // Override this method if you have a non-solid background. (eg: a pattern)
  431.     if ( inUseGrays ) {
  432.         ::RGBBackColor( &mBackground );
  433.         ::EraseRect( &inRect );
  434.     }
  435. }
  436.  
  437. void    CAGASlider::DrawTickMarks( UInt16 inNumber, EIndicatorState inState, Boolean inUseGrays )
  438. {
  439.     if ( inNumber == 0 ) return;
  440.     if ( inNumber == 1 ) inNumber = mMaxValue - mMinValue + 1;
  441.     if ( inNumber <= 1 ) return;
  442.     // Only "pointy" indicators have tickmarks.
  443.     if ( mDirection == ind_Horizontal || mDirection == ind_Vertical ) return;
  444.     
  445.     Rect theTrack;
  446.     GetTrackRect( theTrack );
  447.     
  448.     // Calculate the first tick mark rect, and the last tick mark position
  449.     Rect firstTick;
  450.     Point p = ValueToPosition( mMinValue );    // pos of 1st tick
  451.     Point q = ValueToPosition( mMaxValue );    // pos of last tick
  452.     switch ( mDirection ) {    // SetRect( r, left, top, right, bottom ), SetPt( p, h, v )
  453.     case ind_North:
  454.         p.v -= kTrackToTick + kTickLen + 1;
  455.         q.v -= kTrackToTick + kTickLen + 1;
  456.         ::SetRect( &firstTick, p.h, p.v, p.h + 1, p.v + kTickLen );
  457.         break;
  458.     case ind_South:
  459.         p.v += kTrackToTick;
  460.         q.v += kTrackToTick;
  461.         ::SetRect( &firstTick, p.h, p.v, p.h + 1, p.v + kTickLen );
  462.         break;
  463.     case ind_East:
  464.         p.h += kTrackToTick;
  465.         q.h += kTrackToTick;
  466.         ::SetRect( &firstTick, p.h, p.v, p.h + kTickLen, p.v + 1 );
  467.         break;
  468.     case ind_West:
  469.         p.h -= kTrackToTick + kTickLen + 1;
  470.         q.h -= kTrackToTick + kTickLen + 1;
  471.         ::SetRect( &firstTick, p.h, p.v, p.h + kTickLen, p.v + 1 );
  472.         break;
  473.     case ind_Horizontal:
  474.     case ind_Vertical:
  475.     default:
  476.         SignalPStr_("\pBad Slider Class Member");
  477.     }
  478.     
  479.     Int32 intervals = inNumber - 1;
  480.     Assert_( intervals > 0 );
  481.     for( Int32 i = 0; i <= intervals; i++ ) {
  482.     
  483.         Int16 deltah = (q.h - p.h) * i / intervals;
  484.         Int16 deltav = (q.v - p.v) * i / intervals;
  485.         Rect r = firstTick;
  486.         ::OffsetRect( &r, deltah, deltav );
  487.         
  488.         if ( inUseGrays ) {
  489.         
  490.             // Colors depend on inState
  491.             EGAColor black, gray;
  492.             if ( inState == ind_Disabled ) {
  493.                 gray = ga_4;
  494.                 black = ga_8;
  495.             } else {
  496.                 gray = ga_7;
  497.                 black = ga_Black;
  498.             }
  499.             
  500.             PaintRect( r, black );
  501.  
  502. #if AGA_VERSION==2
  503.             if ( inState != ind_Disabled ) {
  504.                 ::InsetRect( &r, -1, -1 );
  505.                 UGrayscaleAppearance::FrameRect( r, ga_White, gray, ga_Background );
  506.             }
  507. #else
  508.             ::InsetRect( &r, -1, -1 );
  509.             UGrayscaleAppearance::FrameRect( r, ga_White, gray, ga_Background );
  510. #endif        
  511.             
  512.         } else {
  513.         
  514.             if (inState == ind_Disabled) 
  515.                 ::PenPat(&UQDGlobals::GetQDGlobals()->gray);
  516.             ::PaintRect( &r );
  517.             
  518.         }
  519.     }
  520. }
  521.  
  522. CAGASlider::EOrientation CAGASlider::GetOrientation(void)
  523. {
  524.     EOrientation theResult;
  525.     switch ( mDirection ) {
  526.     case ind_Horizontal:
  527.     case ind_North:
  528.     case ind_South:
  529.         theResult = orient_Horizontal;
  530.         break;
  531.     case ind_Vertical:
  532.     case ind_East:
  533.     case ind_West:
  534.         theResult = orient_Vertical;
  535.         break;
  536.     default:
  537.         SignalPStr_("\pBad Slider Class Member");
  538.     }
  539.     return theResult;
  540. }
  541.  
  542. void CAGASlider::ValueToIndicatorRect( Int32 inValue, Rect &outRect )
  543. {
  544.     Point center = ValueToPosition( inValue );
  545.     
  546.     switch ( mDirection ) {
  547.     case ind_Horizontal:
  548.         //    SetRect() arguments are: Rect*, left, top, right, bottom
  549.         ::SetRect( &outRect, center.h - 6, center.v - 8, center.h + 7, center.v + 8 );
  550.         break;
  551.     case ind_North:
  552.         ::SetRect( &outRect, center.h - 7, center.v -10, center.h + 8, center.v + 6 );
  553.         break;
  554.     case ind_South:
  555.         ::SetRect( &outRect, center.h - 7, center.v - 7, center.h + 8, center.v + 9 );
  556.         break;
  557.     case ind_Vertical:
  558.         ::SetRect( &outRect, center.h - 8, center.v - 6, center.h + 8, center.v + 7 );
  559.         break;
  560.     case ind_East:
  561.         ::SetRect( &outRect, center.h - 7, center.v - 7, center.h + 9, center.v + 8 );
  562.         break;
  563.     case ind_West:
  564.         ::SetRect( &outRect, center.h -10, center.v - 7, center.h + 6, center.v + 8 );
  565.         break;
  566.     default:
  567.         SignalPStr_("\pBad Slider Class Member");
  568.     }
  569. }
  570.  
  571. Point    CAGASlider::ValueToPosition( Int32 inValue )
  572. {    // find the position of the center of the indicator that corresponds to inValue
  573.     Point p;    // the result
  574.     
  575.     Assert_( mMinValue <= inValue && inValue <= mMaxValue );
  576.     
  577.     Int32 valueRange = mMaxValue - mMinValue;
  578.     Int16 pixelRange;
  579.     
  580.     // Handle special case to avoid a divide by zero later.
  581.     if ( valueRange <= 0 ) {
  582.         inValue = mMinValue;
  583.         valueRange = 1;
  584.     }
  585.     
  586.     // Flip the direction of increasing value, if asked.
  587.     Int32 startValue;
  588.     if ( mMinIsBottomOrRight ) {
  589.         startValue = mMaxValue;
  590.         valueRange = - valueRange;
  591.     } else {
  592.         startValue = mMinValue;
  593.     }
  594.     
  595.     Rect theTrack;
  596.     GetTrackRect( theTrack );
  597.     
  598.     // Non-pointy indicators (which have no tick marks) aren't as 
  599.     // wide, so we adjust the placement.
  600.     Int16 margin = kEndMargin;
  601.     if ( mDirection == ind_Horizontal || mDirection == ind_Vertical ) margin--;
  602.     
  603.     switch( GetOrientation() ) {
  604.     case orient_Horizontal:
  605.         theTrack.right--;    // aesthetic adjustments
  606.         theTrack.bottom++;
  607.         pixelRange = theTrack.right - theTrack.left - 2*margin;
  608.         p.h = theTrack.left + margin + 
  609.                 pixelRange * (inValue - startValue) / valueRange;
  610.         p.v = ( theTrack.top + theTrack.bottom )/2;
  611.         break;
  612.     case orient_Vertical:
  613.         theTrack.bottom--;    // aesthetic adjustments
  614.         theTrack.right++;
  615.         pixelRange = theTrack.bottom - theTrack.top - 2*margin;
  616.         p.v = theTrack.top + margin + 
  617.                 pixelRange * (inValue - startValue) / valueRange;
  618.         p.h = ( theTrack.right + theTrack.left )/2;
  619.         break;
  620.     default:
  621.         SignalPStr_("\pBad Slider Orientation");
  622.         break;
  623.     }
  624.     
  625.     return p;
  626. }
  627.  
  628. Int32    CAGASlider::PositionToValue( Point inPosition )
  629. {
  630.     // Handle special case to avoid a divide by zero later.
  631.     if ( mMaxValue <= mMinValue ) return mMinValue;
  632.     
  633.     Rect theTrack;
  634.     GetTrackRect( theTrack );
  635.     
  636.     // If we move the mouse too far away from the slider, 
  637.     // return the slider's old value (thus aborting the slide).
  638.     Point offside = {0,0};    // number of pixels off to the side of the slider
  639.     
  640.     if ( inPosition.v < theTrack.top )        offside.v = theTrack.top - inPosition.v;
  641.     if ( inPosition.v > theTrack.bottom )    offside.v = inPosition.v - theTrack.bottom;
  642.     if ( inPosition.h < theTrack.left )        offside.h = theTrack.left - inPosition.h;
  643.     if ( inPosition.h > theTrack.right )    offside.h = inPosition.h - theTrack.right;
  644.     
  645.     if ( (offside.h > kOffsideCutoff) || (offside.v > kOffsideCutoff) )
  646.         return GetValue();    // we're "offside" -- return the slider's current value
  647.     
  648.     // Calculate the value.
  649.     
  650.     Int32 offset, range, result;
  651.     
  652.     switch( GetOrientation() ) {
  653.     case orient_Horizontal:
  654.         offset = inPosition.h - theTrack.left - kEndMargin;
  655.         range = theTrack.right - theTrack.left - 2*kEndMargin;
  656.         Assert_( range > 0 );
  657.         break;
  658.     case orient_Vertical:
  659.         offset = inPosition.v - theTrack.top - kEndMargin;
  660.         range = theTrack.bottom - theTrack.top - 2*kEndMargin;
  661.         Assert_( range > 0 );
  662.         break;
  663.     default:
  664.         SignalPStr_("\pBad Slider Orientation");
  665.         break;
  666.     }
  667.     
  668.     // Flip the direction of increasing value, if asked.
  669.     if ( mMinIsBottomOrRight )
  670.         offset = range - offset;
  671.     
  672.     // Adjust the offset to center the indicator-value on a tick mark instead of between them.
  673.     offset += range / ( mMaxValue - mMinValue ) / 2;
  674.     
  675.     // calculate result
  676.     result = mMinValue + ( mMaxValue - mMinValue ) * offset / range;
  677.     
  678.     // range clipping
  679.     if ( result < mMinValue ) result = mMinValue;
  680.     if ( result > mMaxValue ) result = mMaxValue;
  681.     
  682.     return result;
  683. }
  684.  
  685. void    CAGASlider::ResizeFrameBy(Int16 inWidthDelta, Int16 inHeightDelta, Boolean inRefresh)
  686. {
  687.     LControl::ResizeFrameBy( inWidthDelta, inHeightDelta, inRefresh);
  688.     CalcTrackRect();
  689. }
  690.  
  691. void    CAGASlider::MoveBy(Int32 inHorizDelta, Int32 inVertDelta, Boolean inRefresh)
  692. {
  693.     LControl::MoveBy( inHorizDelta, inVertDelta, inRefresh);
  694.     CalcTrackRect();
  695. }
  696.  
  697. void    CAGASlider::GetTrackRect( Rect &outRect )
  698. {
  699.     // Optimize by caching the track rectangle. (New in slider v1.3.1)
  700.     outRect = mTrackRect;
  701. }
  702.  
  703. void    CAGASlider::CalcTrackRect()
  704. {    // Calculate the rectange to draw the track into.
  705.     //
  706.     // This is the rect of the small black RoundRect. The white and gray highlights
  707.     // are outside this rect. Try to base all drawing on this rect.
  708.     //
  709.     // The compass direction sliders are pushed to the side of the pane away from the point.
  710.     // The plain sliders are centered.
  711.     
  712.     CalcLocalFrameRect( mTrackRect );
  713.     
  714.     SDimension16 theSize;
  715.     GetFrameSize( theSize );
  716.  
  717.     // Center the track in the frame
  718.     switch ( GetOrientation() ) {
  719.     
  720.     case orient_Horizontal:
  721.         
  722.         switch ( mDirection ) {
  723.         case ind_Horizontal:
  724.             mTrackRect.top += (theSize.height - kTrackWidth)/2;
  725.             break;
  726.         case ind_North:
  727.             mTrackRect.top = mTrackRect.bottom - kFlatMargin - kTrackWidth;
  728.             break;
  729.         case ind_South:
  730.             mTrackRect.top += kFlatMargin;
  731.             break;
  732.         }
  733.         mTrackRect.bottom = mTrackRect.top + kTrackWidth;
  734.         mTrackRect.left++;
  735.         mTrackRect.right--;
  736.         break;
  737.         
  738.     case orient_Vertical:
  739.     
  740.         switch ( mDirection ) {
  741.         case ind_Vertical:
  742.             mTrackRect.left += (theSize.width - kTrackWidth)/2;
  743.             break;
  744.         case ind_East:
  745.             mTrackRect.left += kFlatMargin;
  746.             break;
  747.         case ind_West:
  748.             mTrackRect.left = mTrackRect.right - kFlatMargin - kTrackWidth;
  749.             break;
  750.         }
  751.         mTrackRect.right = mTrackRect.left + kTrackWidth;
  752.         mTrackRect.top++;
  753.         mTrackRect.bottom--;
  754.         break;
  755.     }
  756. }
  757.  
  758. Int16    CAGASlider::FindHotSpot(Point inPoint)    // Rewrite w/o icons !!!
  759. {
  760.     // default is None.
  761.     Int16 result = hot_None;
  762.     
  763.     // Is it in the track?
  764.     Rect r;
  765.     GetTrackRect( r );
  766.     ::InsetRect( &r, -2, -2 );
  767.     
  768.     if ( ::PtInRect( inPoint, &r ) ) result = hot_Track;
  769.     
  770.     // Is it in the indicator? If so, override track.
  771.     ValueToIndicatorRect( GetValue(), r );
  772.     
  773.     // code which relys on the PICT mask
  774.     if ( ::PtInRect( inPoint, &r ) ) {
  775.     
  776.         Point p;
  777.         p.h = inPoint.h - r.left;
  778.         p.v = inPoint.v - r.top + mDirection * kIndSize;
  779.         
  780.         // test the value of the pixel in the mask PICT (is there a toolbox call for this?)
  781.         InitMaskData();
  782.         PixMapHandle pm = ::GetGWorldPixMap( sMaskData->GetMacGWorld() );
  783.         Assert_( pm != nil );
  784.         
  785.         Ptr aPtr = ::GetPixBaseAddr( pm );
  786.         Int16 rowBytes = (*pm)->rowBytes & 0x3FFF;
  787.         aPtr += rowBytes * p.v + (p.h/8);
  788.         
  789.         if ( *aPtr & (1<<(7 - (p.h%8))) ) 
  790.             result = hot_Indicator;
  791.     }
  792.     
  793.     return result;
  794. }
  795.  
  796. Boolean    CAGASlider::PointInHotSpot(Point inPoint, Int16 inHotSpot)
  797. {
  798.     if ( inHotSpot == 0 ) return false;
  799.     
  800.     return ( FindHotSpot( inPoint ) == inHotSpot );
  801. }
  802.  
  803. void    CAGASlider::HotSpotAction(Int16 inHotSpot, Boolean inCurrInside, Boolean inPrevInside)
  804. {    // Not used? Keep it for SimulateHotSpotClick().
  805.     if ( inHotSpot != 1 ) return;    // sanity check
  806.     
  807.     if ( inCurrInside != inPrevInside ) {
  808.         if ( inCurrInside ) mState = ind_Pressed;
  809.         else mState = ind_Enabled;
  810.         Draw( nil );
  811.     }
  812. }
  813.  
  814. void    CAGASlider::HotSpotResult(Int16 inHotSpot)
  815. {
  816.     if (inHotSpot != 1) return;
  817.     
  818.     // done dragging, un-press the indicator
  819.     mState = ind_Enabled;
  820.     Draw( nil );
  821. }
  822.  
  823. Boolean    CAGASlider::TrackHotSpot(Int16 inHotSpot, Point inPoint)
  824. {
  825. #ifndef Debug_Signal
  826.     // inHotSpot is only used in the Assert_(), and therefore only if Debug_Signal is defined.
  827.     #pragma unused(inHotSpot)
  828. #endif
  829.     Assert_ ( inHotSpot == hot_Indicator || inHotSpot == hot_Track );
  830.     
  831. #ifdef __PROFILER__
  832.     Boolean profilerIsOn = ProfilerGetStatus();
  833.     ProfilerSetStatus(true);    // turn on the profiler
  834. #endif
  835.  
  836.     mIsTracking = true;        // should this be a "St" object?
  837.     mTrackingValue = GetValue();
  838.     
  839.     Rect    oldRect, newRect;
  840.     ValueToIndicatorRect(mTrackingValue, newRect);
  841.     
  842.     // "Press" the indicator
  843.     mState = ind_Pressed;    // should this be a "St" object?
  844.     mChangedRect = newRect;
  845.     Draw( nil );
  846.     
  847.     // Track the mouse while it is down
  848.     Point    currPt = inPoint;
  849.     while (StillDown()) {
  850.     
  851.         ::GetMouse(&currPt);
  852.         
  853.         // calculate ghost position
  854.         Int32 currValue = PositionToValue( currPt );
  855.         
  856.         if ( currValue != mTrackingValue ) {
  857.         
  858.             mTrackingValue = currValue;
  859.             
  860.             if ( mContinuousBroadcast ) {
  861.             
  862.                 // this code modified from LControl::BroadcastValueMessage()
  863.                 if (mValueMessage != msg_Nothing) {
  864.                     Int32    value = mTrackingValue;    // broadcast tracking value
  865.                     BroadcastMessage(mValueMessage, (void*) &value);
  866.                 }
  867.                 
  868.             }
  869.             
  870.             // Optimize by remembering the rect that has changed. (new in slider v 1.3.1)
  871.             oldRect = newRect;
  872.             ValueToIndicatorRect(mTrackingValue, newRect);
  873.             ::UnionRect( &oldRect, &newRect, &mChangedRect );
  874.             
  875.             // Note: Draw() must come after BroadcastMessage() since some LListener
  876.             // might do some drawing of it's own and change the drawing focus.
  877.             // We could also just call FocusDraw(), but this saves a few cycles.
  878.             Draw( nil );
  879.         }
  880.     }
  881.     
  882.     mState = ind_Enabled;
  883.                                     // Check if MouseUp occurred in HotSpot
  884.     EventRecord    macEvent;            // Get location from MouseUp event
  885.     if (::GetOSEvent(mUpMask, &macEvent)) {
  886.         currPt = macEvent.where;
  887.         ::GlobalToLocal(&currPt);
  888.         mTrackingValue = PositionToValue( currPt );
  889.     }
  890.     
  891.     mIsTracking = false;    // must be reset before the final Draw()
  892.     
  893.     // Note: SetValue() only redraws the slider if the value changed.
  894.     // Since mState has changed, we want to make sure that it's redrawn
  895.     // and yet we don't want to draw twice.
  896.     if (GetValue() != mTrackingValue) {
  897.         SetValue( mTrackingValue );    // SetValue() calls BroadcastValueMessage()
  898. #ifndef SLIDER_DRAWS_SELF
  899.         Draw( nil );
  900. #endif // SLIDER_DRAWS_SELF
  901.     } else {
  902.         Draw(nil);
  903.     }
  904.     
  905. #ifdef __PROFILER__
  906.     ProfilerSetStatus(profilerIsOn);    // turn off the profiler
  907. #endif
  908.     
  909.     return true;    // tell caller that mouse was released in hot-spot
  910. }
  911.  
  912. #ifdef SLIDER_DRAWS_SELF
  913. void    CAGASlider::SetValue( Int32 inValue )
  914. {    // If the value changes, pass to superclass and redraw.
  915.     
  916.     if ( inValue != GetValue() ) {
  917.         LControl::SetValue( inValue );
  918.         Draw(nil);
  919.     }
  920. }
  921.  
  922. void    CAGASlider::EnableSelf()
  923. {
  924.     Refresh(); 
  925. }
  926.  
  927. void    CAGASlider::DisableSelf()
  928. {
  929.     Refresh(); 
  930. }
  931. #endif // SLIDER_DRAWS_SELF
  932.